#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#include "mpu6050.h"

#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C

#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48

#define PWR_MGMT_1  0x6B

//主设备号
int major = 11;
//次设备号
int minor = 0;
//申请数量
int mpu6050_num = 1;

//自定义字符设备
struct mpu6050_cdev {

	//定义字符设备
	struct cdev mydev;
	struct i2c_client *pclt;
	//class
	struct class * pcls;
	//device
	struct device * pdev;
};


struct mpu6050_cdev * pgdev = NULL;

int read_byte_from_mpu6050(struct i2c_client *pclt, unsigned char reg, unsigned char *pdata){
	
	int ret = 0;

	struct i2c_msg msgs_arr[2] = {
		{pclt->addr, 0, 1, &reg},
		{pclt->addr, I2C_M_RD, 1, pdata}
	};

	ret = i2c_transfer(pclt->adapter, msgs_arr, ARRAY_SIZE(msgs_arr));
	if(ret < 0){
		printk("ret = %d,in mpu6050_read_byte\n",ret);
		return -1;
	}

	return 0;
}

int read_data_from_mpu6050(struct i2c_client *pclt, unsigned char reg_H, unsigned char reg_L, unsigned short * pdata){
	
	int ret = 0;
	unsigned char tempchar = 0;
	unsigned short data = 0;
	ret = read_byte_from_mpu6050(pclt, reg_L, &tempchar);
	if(ret < 0){
		printk("read reg_L 0x%x failed\n",reg_L);
		return -1;
	}
	data = tempchar;
	ret = read_byte_from_mpu6050(pclt, reg_H, &tempchar);
	if(ret < 0){
		printk("read reg_H 0x%x failed\n",reg_H);
		return -1;
	}
	data |= tempchar<<8;
	*pdata = data;
	return 0;

}

int write_to_mpu6050(struct i2c_client *pclt, unsigned char reg, unsigned char data){
	unsigned char wbuf[] = {reg, data};
	int ret = 0;

	struct i2c_msg msgs_arr[1] = {
		{pclt->addr, 0, 2, wbuf}
	};
	
	ret = i2c_transfer(pclt->adapter, msgs_arr, ARRAY_SIZE(msgs_arr));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_write_byte\n",ret);
		return -1;
	}
	return 0;
}

int init_mpu6050(struct i2c_client *pclt){

	int ret = 0;

	ret = write_to_mpu6050(pclt, SMPLRT_DIV, 0x07);
	ret += write_to_mpu6050(pclt, CONFIG, 0x06);
	ret += write_to_mpu6050(pclt, GYRO_CONFIG, 0xF8);
	ret += write_to_mpu6050(pclt, ACCEL_CONFIG, 0x19);
	ret += write_to_mpu6050(pclt, PWR_MGMT_1, 0x00);
	if(ret < 0){
		printk("write init reg failed \n");
		return -1;
	}
	return 0;
}

int mpu6050_open(struct inode *pnode, struct file *pfile){

	//将自定义字符设备的地址赋值给 private_data 变量
	//这样后续的操作函数都不需要使用全局变量
	pfile->private_data = (void *)container_of(pnode->i_cdev, struct mpu6050_cdev, mydev);
	printk("mpu6050_open is called \n");

	return 0;
}

int mpu6050_close(struct inode *pnode, struct file *pfile){
	printk("mpu6050_close is called \n");
	return 0;
}

long mpu6050_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg){

	int ret = 0;
	//拿到自定义字符设备的地址
	struct mpu6050_cdev *pmychardev = (struct mpu6050_cdev *)pfile->private_data;

	union mpu6050_data data;

	switch(cmd){
		case GET_ACCEL:
			ret = read_data_from_mpu6050(pmychardev->pclt, ACCEL_XOUT_H, ACCEL_XOUT_L, &data.accel.x);
			ret += read_data_from_mpu6050(pmychardev->pclt, ACCEL_YOUT_H, ACCEL_YOUT_L, &data.accel.y);
			ret += read_data_from_mpu6050(pmychardev->pclt, ACCEL_ZOUT_H, ACCEL_ZOUT_L, &data.accel.z);
			break;
		case GET_GYRO:
			ret = read_data_from_mpu6050(pmychardev->pclt, GYRO_XOUT_H, GYRO_XOUT_L, &data.gyro.x);
			ret += read_data_from_mpu6050(pmychardev->pclt, GYRO_YOUT_H, GYRO_YOUT_L, &data.gyro.y);
			ret += read_data_from_mpu6050(pmychardev->pclt, GYRO_ZOUT_H, GYRO_ZOUT_L, &data.gyro.z);
			break;
		case GET_TEMP:
			ret = read_data_from_mpu6050(pmychardev->pclt, TEMP_OUT_H, TEMP_OUT_L, &data.temp);
			break;
		default:
			ret = -1;
			break;
	}

	if(ret < 0){
		printk("read data from mpu6050 failed \n");
		return -EINVAL;
	}

	if(copy_to_user((void *)arg, &data, sizeof(data))){
		return -EFAULT;
	}

	return 0;
}


//定义操作函数
struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_close,
	.unlocked_ioctl = mpu6050_ioctl,
};

int mpu6050_probe(struct i2c_client *pclt, const struct i2c_device_id *pid)
{
	int ret = 0;
	//设备号
	dev_t devno = MKDEV(major, minor);
	//从当前设备号开始验证，mpu6050_num数量的设备号。若系统没有占用，则申请占用mpu6050_num数量的设备号
	//"mpu6050": 该设备号在/proc/devices文件中的名称。意义：确保设备号申请成功，及方便后续据此设备号、名称创建设备文件
	ret = register_chrdev_region(devno, mpu6050_num, "mpu6050");

	if(ret){
		//系统动态分配
		ret = alloc_chrdev_region(&devno, minor, mpu6050_num, "mpu6050");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		//此时设备号是由系统分配的
		major = MAJOR(devno);
	}

	pgdev =(struct mpu6050_cdev *) kmalloc(sizeof(struct mpu6050_cdev), GFP_KERNEL);
	if(pgdev == NULL){
		printk("kmalloc free failed \n");
		//释放设备号
		unregister_chrdev_region(devno, mpu6050_num);
		return -1;
	}
	memset(pgdev, 0, sizeof(struct mpu6050_cdev));

	pgdev->pclt = pclt;

	//创建class
	pgdev->pcls = class_create(THIS_MODULE, "mpu6050class");
	//创建device
	pgdev->pdev = device_create(pgdev->pcls, NULL, devno, NULL, "fsmpu60500");

	//初始化字符设备,指定所属内核模块，指定操作函数集
	pgdev->mydev.owner = THIS_MODULE;
	cdev_init(&pgdev->mydev, &myops);

	//将cdev对象添加到内核的字符设备链表中
	cdev_add(&pgdev->mydev, devno, mpu6050_num);

	init_mpu6050(pclt);

	return 0;
}

int mpu6050_remove(struct i2c_client *pclt)
{
	dev_t devno = MKDEV(major, minor);
	
	//从内核的字符设备链表中移除该设备
	cdev_del(&pgdev->mydev);

	device_destroy(pgdev->pcls, devno);
	class_destroy(pgdev->pcls);

	//释放kmalloc的空间
	kfree(pgdev);
	pgdev = NULL;

	//释放设备号
	unregister_chrdev_region(devno, mpu6050_num);

	return 0;
}

struct of_device_id mpu6050_dt[] = 
{
	{.compatible = "invensense,mpu6050"},
	{}
};

struct i2c_device_id mpu6050_ids[] = 
{
	    {"mpu6050",0},
		{}
};

struct i2c_driver mpu6050_driver = {
	.probe = mpu6050_probe,
	.remove = mpu6050_remove,
	.driver = {
		.name = "mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_dt
	},
	.id_table = mpu6050_ids
};

//宏定义，展开后就是模块入口函数和模块出口函数
#if 0
int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);
		}

void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver_exitver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
#else
module_i2c_driver(mpu6050_driver);
#endif

MODULE_LICENSE("Dual BSD/GPL");
